<template>
  <g
    ref="gEl"
    :data-x="dragX"
    :data-y="dragY"
    :transform="`translate(${dragX},${dragY}) rotate(${rotate},${centerPoint.x},${centerPoint.y})`"
    fill="#f2f2f2"
    :data-component="componentName"
  >
    <Component
      :is="component"
      v-if="component"
      :x="x"
      :y="y"
      :opacity="opacity"
      :width="width"
      :style="{ cursor: selected && isEditor ? 'move' : 'pointer' }"
    />
    <image
      v-else-if="imgSrc"
      :xlink:href="imgSrc"
      :x="x"
      :y="y"
      :opacity="opacity"
      :width="width"
      @mousedown="dragStart"
      @mouseover="mouseover"
      @mousemove="mousemove"
      @mouseout="mouseout"
      @mouseup="mouseup"
      @contextmenu="contextmenu"
      @dblclick="setText"
    />
    <!-- 高亮连接点 -->
    <circle
      v-if="curConnectPoint"
      :cx="curConnectPoint.x"
      :cy="curConnectPoint.y"
      r="6"
      fill="#4F80FF"
      stroke="rgba(255,255,255,0.8)"
      stroke-width="2"
    />
    <template v-if="selected">
      <rect
        :x="rects[0].x + OperSize / 2"
        :y="rects[0].y + OperSize / 2"
        :width="rects[1].x - rects[0].x"
        :height="rects[2].y - rects[0].y"
        fill="none"
        stroke="#4F80FF"
        stroke-dasharray="3 3"
        :style="{ 'stroke-width': isEditor ? 1 : 2 }"
        pointer-events="none"
      />
      <template v-if="isEditor">
        <!-- <rect
          v-for="(el, i) in rects"
          :key="el.x + '' + el.y + i"
          rx="4"
          ry="4"
          fill="#4F80FF"
          :x="el.x"
          :y="el.y"
          :width="OperSize"
          :height="OperSize"
          :style="{ cursor: OPER_CURSORS[i] }"
          @mousedown="rectDragStart($event, i)"
        /> -->
        <ResizeHandle
          v-for="(el, i) in rects"
          :key="el.x + '' + el.y + i"
          :x="el.x"
          :y="el.y"
          :cursor="OPER_CURSORS[i]"
          @mousedown="rectDragStart($event, i)"
        />
        <foreignObject
          :x="(rects[0].x + OperSize + rects[1].x) / 2 - OperSize"
          :y="rects[0].y - OperSize * 2"
          :width="OperSize * 2"
          :height="OperSize * 2"
          style="cursor: move"
          @mousedown="rotateStart"
        >
          <svg :width="OperSize * 2" :height="OperSize * 2">
            <use xlink:href="#icon-rotate" style="fill: #333"></use>
          </svg>
        </foreignObject>
      </template>
    </template>
    <!-- component的占位 -->
    <rect
      v-if="component"
      :x="x"
      :y="y"
      :width="width"
      :height="width / factor"
      fill="rgba(0,0,0,0)"
      stroke="rgba(0,0,0,0)"
      @mousedown="dragStart"
      @mouseover="mouseover"
      @mousemove="mousemove"
      @mouseout="mouseout"
      @mouseup="mouseup"
      @contextmenu="contextmenu"
      @dblclick="setText"
    />
    <EditableText
      v-model:value="textValue"
      v-model:width="textWidth"
      v-model:height="textHeight"
      v-model:editable="textEditable"
      :x="textX"
      :y="textY"
    />
  </g>
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, inject, onBeforeUnmount, ref, watch } from "vue";
import {
  CompCommon,
  CompCommonKey,
  ConfigurationInstance,
  DeviceJSON,
  DragMoveOptions,
  Point,
  SelectEvent,
  TextPosition,
} from "/@/components/types";
import { OperSize } from "/@/components/common";
import { ImgDetail, SvgDetail } from "/@/components/Device/resourceDetail";
import EditableText from "/@/components/base/EditableText.vue";
import { ContextmenuItem, ConfigurationType, SvgInfo } from "/@/Configuration/types";
import { useDrag } from "/@/hooks/useDrag";
import { getRotatePoint, isRectIntersect } from "/@/utils/draw";
import { useDeviceRotate } from "./useDeviceRotate";
import { useConnEffect } from "/@/hooks/useConnEffect";
import ResizeHandle from "/@/components/base/ResizeHandle.vue";
import { DevicePanelInfo } from "/@/Configuration/AttrPanel/types";

const props = defineProps<{
  index: number;
  id: string;
  centerX: number;
  centerY: number;
  initWidth: number;
  scaleFactor: number;
  componentName?: string;
  imgSrc?: string;
  imgName?: string;
}>();
const emits = defineEmits<{
  (event: "select", e: SelectEvent): void;
}>();

let component: any = null;
let factor = 1;
let connectPosition: number[][] = [];
let type = ConfigurationType.DeviceSvg;
if (props.componentName) {
  component = defineAsyncComponent(() => import(`./resource/${props.componentName}.vue`));
  factor = SvgDetail[props.componentName].factor;
  connectPosition = SvgDetail[props.componentName].connectPosition;
} else if (props.imgSrc && props.imgName) {
  factor = ImgDetail[props.imgName].factor;
  connectPosition = ImgDetail[props.imgName].connectPosition;
  type = ConfigurationType.DeviceImage;
}

//最小宽度
const MIN_SIZE = 20;
//默认宽度
const DEFAULT_SIZE = 150;

const OPER_CURSORS = ["nw-resize", "ne-resize", "sw-resize", "se-resize"];

const compCommon = inject(CompCommonKey) as CompCommon;
const { isEditor } = compCommon;

const svgInfo = compCommon.getSvgInfo() as SvgInfo;
//宽高比系数
const whFactor = ref(factor);
//只确定宽度
let initWidth = props.initWidth || DEFAULT_SIZE;
let _width = initWidth;
let _x = props.centerX - initWidth / 2;
let _y = props.centerY - initWidth / whFactor.value / 2;
initWidth = initWidth * props.scaleFactor;
const width = ref(initWidth);
const x = ref(props.centerX - initWidth / 2);
const y = ref(props.centerY - initWidth / whFactor.value / 2);
//实时高度
const height = computed(() => {
  return width.value / whFactor.value;
});
const selected = ref(false);
const rects = computed<Point[]>(() => [
  {
    x: x.value - OperSize / 2,
    y: y.value - OperSize / 2,
  },
  {
    x: x.value + width.value - OperSize / 2,
    y: y.value - OperSize / 2,
  },
  {
    x: x.value - OperSize / 2,
    y: y.value + width.value / whFactor.value - OperSize / 2,
  },
  {
    x: x.value + width.value - OperSize / 2,
    y: y.value + width.value / whFactor.value - OperSize / 2,
  },
]);
const centerPoint = computed<Point>(() => ({
  x: x.value + width.value / 2,
  y: y.value + width.value / whFactor.value / 2,
}));
watch(
  () => props.scaleFactor,
  (v) => {
    x.value = _x * v;
    y.value = _y * v;
    width.value = _width * v;
  }
);
function getDeviceInfo(): DevicePanelInfo {
  return {
    x: x.value,
    y: y.value,
    width: width.value,
    height: height.value,
    rotate: rotate.value,
    componentName: props.componentName,
    imgSrc: props.imgSrc,
    setRotate(deg) {
      rotate.value = deg;
    },
  };
}
/**
 * drag device
 */
const dragX = ref(0);
const dragY = ref(0);

function dragMove({ dx, dy }: DragMoveOptions) {
  dragX.value = dx as number;
  dragY.value = dy as number;
}

function dragEnd() {
  x.value += dragX.value;
  y.value += dragY.value;
  _x = x.value / props.scaleFactor;
  _y = y.value / props.scaleFactor;
  dragX.value = 0;
  dragY.value = 0;
}

function selectDevice() {
  selected.value = true;
  compCommon.toggleSelect({
    type,
    selected: true,
    index: props.index,
    unSelect() {
      selected.value = false;
    },
    getDeviceInfo,
  });
}
/**
 * drag device
 */
const { dragStart } = useDrag<[], { isDragSelect: boolean }>({
  unStopPropagation: !isEditor.value,
  element: svgInfo.container,
  start(_startData, e) {
    const isDragSelect = compCommon.multipleSelect.startTogether(props.id);
    if (!isDragSelect) {
      compCommon.connection.startTogether(props.id);
      selectDevice();
      emits("select", {
        e,
        type,
        id: props.id,
        componentName: props.componentName,
        imgSrc: props.imgSrc,
      });
    }
    return { isDragSelect };
  },
  move({ dx, dy, startResult }) {
    if (compCommon.isEditor.value) {
      if (startResult.isDragSelect) {
        compCommon.multipleSelect.moveTogether({ dx, dy });
      } else {
        dragMove({ dx, dy });
        compCommon.connection.moveTogether(props.id, { dx, dy });
      }
    }
  },
  end({ startResult, isClick }, e) {
    if (compCommon.isEditor.value) {
      if (startResult.isDragSelect) {
        compCommon.multipleSelect.endTogether();
      } else {
        dragEnd();
        compCommon.connection.endTogether(props.id);
      }
    }
  },
});
/**
 * drag device's rect
 */
const { dragStart: rectDragStart } = useDrag<number[], { width: number; height: number }>({
  getRelativeElement() {
    return svgInfo.container;
  },
  start() {
    return {
      width: width.value,
      height: height.value,
    };
  },
  move({ dx, dy, startData, startResult }) {
    const curDragRectIndex = startData.args[0];
    if (selected.value) {
      // const isX = Math.abs(dx) > Math.abs(dy);
      let width1 = width.value,
        width2 = width1;
      if (curDragRectIndex === 0 || curDragRectIndex === 1) {
        if (rects.value[2].y - (startData.y + dy) < MIN_SIZE / whFactor.value) {
          width2 = MIN_SIZE;
        } else {
          width2 = (startResult.height - dy) * whFactor.value;
        }
      }
      if (curDragRectIndex === 0 || curDragRectIndex === 2) {
        if (rects.value[1].x - (startData.x + dx) < MIN_SIZE) {
          width1 = MIN_SIZE;
        } else {
          width1 = startResult.width - dx;
        }
      }
      if (curDragRectIndex === 1 || curDragRectIndex === 3) {
        if (startData.x + dx - rects.value[0].x < MIN_SIZE) {
          width1 = MIN_SIZE;
        } else {
          width1 = startResult.width + dx;
        }
      }
      if (curDragRectIndex === 2 || curDragRectIndex === 3) {
        if (startData.y + dy - rects.value[1].y < MIN_SIZE / whFactor.value) {
          width2 = MIN_SIZE;
        } else {
          width2 = (startResult.height + dy) * whFactor.value;
        }
      }
      const curWidth = Math.max(width1, width2);
      if (curDragRectIndex === 0) {
        x.value = rects.value[1].x + OperSize / 2 - curWidth;
        y.value = rects.value[2].y + OperSize / 2 - curWidth / whFactor.value;
      } else if (curDragRectIndex === 1) {
        y.value = rects.value[2].y + OperSize / 2 - curWidth / whFactor.value;
      } else if (curDragRectIndex === 2) {
        x.value = rects.value[1].x + OperSize / 2 - curWidth;
      }
      width.value = curWidth;
      _x = x.value / props.scaleFactor;
      _y = y.value / props.scaleFactor;
      _width = curWidth / props.scaleFactor;
      const connectPoints = getConnectPoints(true);
      compCommon.connection.moveTogether(props.id, { connectPoints });
    }
  },
});
/**
 * dragging pipePath rect and over
 */
function getConnectPoints(isRotate?: boolean) {
  const connetPoints: Point[] = [];
  connectPosition.forEach((item) => {
    const cx = x.value + width.value * item[0];
    const cy = y.value + height.value * item[1];
    const point = {
      x: cx,
      y: cy,
    };
    connetPoints.push(isRotate ? getRotatePoint(centerPoint.value, point, rotate.value) : point);
  });
  return connetPoints;
}
function getDragInfo(connectPositionIndex?: number) {
  return {
    id: props.id,
    type,
    move: dragMove,
    end: dragEnd,
    extra: {
      connectPositionIndex,
    },
  };
}
const { opacity, curConnectPoint, mousemove, mouseout, mouseover, mouseup } = useConnEffect({
  getConnectPoints() {
    return getConnectPoints(true);
  },
  getDraggingRect() {
    return compCommon?.draggingPipePathRect;
  },
  setDragInfo(index) {
    if (compCommon) {
      compCommon.connectingDevice = getDragInfo(index);
    }
  },
});
/**
 * 删除的时候 解除连接 反选
 */
onBeforeUnmount(() => {
  compCommon.connection.disConnect(props.id);
  if (selected.value) {
    compCommon.toggleSelect({
      type,
      selected: false,
      index: props.index,
    });
  }
});
/**
 * deviceText
 */
const TEXT2DEVICE = 5;
const textPosition = ref<TextPosition>(TextPosition.Bottom);
const textValue = ref("");
const textHeight = ref(0);
const textWidth = ref(0);
const textEditable = ref(false);
const textX = computed(() => {
  if (textPosition.value === TextPosition.Left) {
    return x.value - TEXT2DEVICE - textWidth.value;
  } else if (textPosition.value === TextPosition.Right) {
    return x.value + width.value + TEXT2DEVICE;
  } else {
    return x.value + width.value / 2 - textWidth.value / 2;
  }
});
const textY = computed(() => {
  if (textPosition.value === TextPosition.Top) {
    return y.value - TEXT2DEVICE - textHeight.value;
  } else if (textPosition.value === TextPosition.Bottom) {
    return y.value + width.value / whFactor.value + TEXT2DEVICE;
  } else {
    return y.value + width.value / whFactor.value / 2 - textHeight.value / 2;
  }
});
function setText() {
  if (isEditor.value) {
    textEditable.value = true;
  }
}
/**
 * rotate
 */
const { rotateStart, rotate } = useDeviceRotate({
  compCommon,
  centerPoint,
  deviceId: props.id,
  getConnectPoints,
});
/**
 * 更新面板信息
 */
watch([width, x, y, rotate], (v) => {
  compCommon.updatePanelInfo({
    type,
    panelInfo: {
      width: v[0],
      height: height.value,
      x: v[1],
      y: v[2],
      rotate: v[3],
      setRotate(deg) {
        rotate.value = deg;
      },
    },
  });
});
/**
 *
 */
const deviceExpose: ConfigurationInstance<DeviceJSON> = {
  isInBoxSelect(boxSelect) {
    const isIn = isRectIntersect(
      { x: x.value, y: y.value },
      width.value,
      height.value,
      { x: boxSelect.x, y: boxSelect.y },
      boxSelect.width,
      boxSelect.height
    );
    return isIn;
  },
  select() {
    selected.value = true;
    compCommon.toggleSelect({
      type,
      selected: true,
      index: props.index,
      unSelect() {
        selected.value = false;
      },
      multiple: true,
      id: props.id,
      drag: {
        move: dragMove,
        end: dragEnd,
      },
      getDeviceInfo,
    });
  },
  toJSON() {
    const json: DeviceJSON = {
      id: props.id,
      //取原始的_x _y _width，不能直接取x y width，因为可能是缩放过的
      x: _x,
      y: _y,
      width: _width,
      rotate: rotate.value,
      text: textValue.value,
      textPosition: textPosition.value,
    };
    if (props.imgSrc) {
      json.imgSrc = props.imgSrc;
      json.imgName = props.imgName;
    }
    if (props.componentName) {
      json.componentName = props.componentName;
    }
    return json;
  },
  setJSON(data) {
    rotate.value = data.rotate;
    textValue.value = data.text || "";
    const pos = data.textPosition;
    if (pos) {
      textPosition.value = pos;
    }
  },
  getDragInfo,
  previewCondition() {},
  restoreCondition() {},
  getOriginalProps() {},
  getBox() {
    return {
      x: x.value,
      y: y.value,
      width: width.value,
      height: height.value,
      maxX: x.value + width.value,
      maxY: y.value + height.value,
    };
  },
  translate(dx, dy) {
    x.value = x.value + dx;
    y.value = y.value + dy;
    _x = x.value / props.scaleFactor;
    _y = y.value / props.scaleFactor;
  },
};

defineExpose(deviceExpose);

const gEl = ref<SVGGElement>();
function contextmenu(e: MouseEvent) {
  const arr: ContextmenuItem[] = [
    {
      name: "设置文字",
      handler() {
        textEditable.value = true;
      },
    },
    {
      name: "文字位置",
      children: [
        {
          name: "上",
          handler() {
            textPosition.value = TextPosition.Top;
          },
        },
        {
          name: "右",
          handler() {
            textPosition.value = TextPosition.Right;
          },
        },
        {
          name: "下",
          handler() {
            textPosition.value = TextPosition.Bottom;
          },
        },
        {
          name: "左",
          handler() {
            textPosition.value = TextPosition.Left;
          },
        },
      ],
    },
  ];
  if (compCommon.connection.isConnect(props.id)) {
    arr.push({
      name: "解除连接",
      handler() {
        compCommon.connection.disConnect(props.id);
      },
    });
  }
  compCommon.showContextmenu(e, { data: arr, id: props.id, element: gEl.value as SVGGElement });
}
</script>
